import java.util.ArrayList;
import java.util.Random;
public class OldTeam8_Player implements MancalaPlayer
{
		public int anticipatedMove = -1;
		public int actualMove = -1;
		public MancalaGameState prev;
        private class tNode
        {
                public ArrayList<tNode> children;
                public MancalaGameState gs;
                public int heuristic_value;
                public int move;
                public int player;
                public int maxplayer;
                public tNode parent = null;
                


                
                public tNode(MancalaGameState gs)
                {
                        this.gs = gs;
                        children = new ArrayList<tNode>();
                        heuristic_value = 0;
                        player = gs.CurrentPlayer;

                }

                public void generateChildren() throws Exception
                {
                        MancalaGameState gs_copy = gs.copy();
                        if(player==0){
                        	for(int i=0;i<gs.cols();i++)
                            {
                                    if(gs.validMove(i))
                                    {
                                    	
                                            gs_copy.play(i);
                                            tNode child = new tNode(gs_copy);
                                            child.move = i; 
                                            child.parent = this;
                                            children.add(child);
                                    }

                                    gs_copy = gs.copy();
                            }
                        }
                        else{
                        	for(int i=gs.cols()-1;i>=0;i--)
                            {
                                    if(gs.validMove(i))
                                    {
                                            gs_copy.play(i);
                                            tNode child = new tNode(gs_copy);
                                            child.move = i; 
                                            child.parent = this;
                                            children.add(child);
                                    }

                                    gs_copy = gs.copy();
                            }
                        }
       
                }
                


                public void addChild(MancalaGameState gs)
                {
                        tNode temp = new tNode(gs);
                        children.add(temp);
                }

                public ArrayList<tNode> getChildren()
                {
                        if(children.size()<=0)
                        {
                                System.out.println("Empty List");
                                return children;
                        }
                        else
                        {
                                return children;
                        }
                }
                
                public String toString(){
                	String s = "The current Player is " + this.player
							+ " and its heurstics are: \n";
					s += "<";
					for (tNode child : children) {
						s += child.heuristic_value + ", ";

					}
					s += ">";
                        return s;
                }
                
                public String to2String(){
                		String s = "The current Player is " + this.player
								+ " and its heurstics are: \n";
						s += "<";
						for (tNode child : children) {
							s += child.heuristic_value + ", ";

						}
						s += ">";
						
						for(tNode child:children){
							s+="the child "+child.move +" has those children \n"; 
							s+= child.toString()+"\n";
							
						}
						s+="------------------------------";
						return s;
                }

				public void computeMinParentValue(int value) {
				//	System.out.println("min");
					parent.heuristic_value = Math.min(parent.heuristic_value,value);
					
				}
				public void computeMaxParentValue(int value) {
					//System.out.println("max");
					parent.heuristic_value = Math.min(parent.heuristic_value,value);
					
				}

				public void computeParentValue(int player, int value) {
					if(player==maxplayer){
						computeMinParentValue(value);
					}
					else{
						computeMaxParentValue(value);
					}
					
				}

				public tNode getSubChildren(int move2) {
					return children.get(5-move);
				}



					
					
			
                

        }

        int player;
        
        private int score;
        private WeightCollection myWeights;
        public int gamesPlayed;
        
        public OldTeam8_Player(int player_num)
        {
                player = player_num;
        }
        
        public OldTeam8_Player(int player_num, WeightCollection weights)
        {
                player = player_num;
                myWeights = weights;
                score = 0;
                gamesPlayed = 0;
        }
        
        
        public int minimax(tNode gametNode, int depth) throws Exception{
        	
        	gametNode.generateChildren();
        	for(tNode child: gametNode.children){
        		child.heuristic_value = evaluate(child.gs);
        	}
        	findFreeMoves(gametNode); 
        	if(gametNode.gs.checkEndGame() || depth == 0){
        		gametNode.heuristic_value = evaluate(gametNode.gs);
        		return gametNode.heuristic_value; 
        	}
        	
        	if(gametNode.player == player){
        		gametNode.heuristic_value = Integer.MIN_VALUE;
        	}
        	else{
        		gametNode.heuristic_value = Integer.MAX_VALUE;
        	}
        	int score;
        	for(tNode child : gametNode.children){
        		child.generateChildren();
        		score = minimax(child, depth--);
        		if(gametNode.player==player){
        			gametNode.computeMaxParentValue(score);
        		}
        		else{
        			gametNode.computeMinParentValue(score);
        		}
        	}
        	System.out.println(gametNode.toString());
			return gametNode.parent.heuristic_value;
        	
        }

        public int alphaBeta(tNode gametNode, int depth, int a, int b, int player) throws Exception
        {
        	gametNode.generateChildren();
                //System.out.println("The game tNode in depth of "+depth+" with move of "+gametNode.move);
                int alpha = a;
                int beta = b;
                for(tNode child : gametNode.children)
                {
                      child.heuristic_value = evaluate(child.gs);
                      child.generateChildren();
                }
                findFreeMoves(gametNode); 
                if(gametNode.gs.checkEndGame()||depth==0){
                        gametNode.heuristic_value = evaluate(gametNode.gs);
                        //gametNode.computeMinimaxForParent(gametNode.heuristic_value);
                        return gametNode.heuristic_value;
                }
                
                if(player == this.player)
                {
                        for(tNode child : gametNode.children)
                        {
                                
                                alpha = Math.max(alpha, alphaBeta(child,depth-1,alpha,beta,1-child.player));
                               // child.heuristic_value = alpha;
                               
                                child.computeMaxParentValue(child.heuristic_value);
                                if(beta<=alpha)
                                {
                                //      System.out.println("Beta Cut-Off");
                                        break;
                                }
                        //System.out.println("Beta of depth "+depth+": ");
                        //child.gs.printState();

                        }
                      //  gametNode.heuristic_value = alpha;
                return alpha;
                }
                else
                {
                        for(tNode child : gametNode.children)
                        {
                                beta= Math.min(beta, alphaBeta(child,depth-1,alpha,beta,1-child.player));
                               // child.heuristic_value = beta;
                                child.computeMinParentValue(child.heuristic_value);
                                if(beta<=alpha)
                                {
                                //      System.out.println("Alpha Cut-Off");
                                        break;
                                }
                                //System.out.println("Alpha of depth "+depth+": ");
                        //      child.gs.printState();
                                
                        }
                      //  gametNode.heuristic_value = beta;
                        return beta;
                }
    }


        public int evaluate(MancalaGameState gs)
        {
                int sd = ScoreDifference(gs);
                int sic = StoneInControl(gs);
                int matc = MaxAmountToCapture(gs);
                int maltc = MaxAmountLoseToCapture(gs);
                
                if(myWeights == null)
                {
                        //preset weights

                        return  (2*sd+(sic*3)+(matc*4)-(maltc*10));
                }
                else
                {
                        /* Needs to be replaced with heuristic corresponding to weights! */
                        return  (myWeights.getWeight(0)*sd+(sic*myWeights.getWeight(1))+(matc*myWeights.getWeight(2))+(maltc*myWeights.getWeight(3)));
                }
        }       
        
        private int MaxAmountLoseToCapture(MancalaGameState gs) {
                int max = 0;

                for(int i = 0; i < 6; i++)
                {
                        
                        if(gs.stonesAt(1-player, i) == 0 && gs.stonesAt(player, i)>0 )
                        {
                                
                                int myStone = gs.stonesAt(player, i);
                                int move = findReachableMoveToBucket(gs,i,1-player);
                                if(move!=-1)
                                {
                                        if(myStone > max)
                                        {       
                                                max = myStone;
                                        }
                                        ///use to debug whether it finds a buckt that can be captured
                                 //       System.out.println("WARNING: There is an opening of "+myStone+" stones to lose at bucket "+i+" with move "+move);
                                   //     gs.printState();
                                        
                                }
                                        
                        }
                }
                
                return max;
        }

        private int MaxAmountToCapture(MancalaGameState gs) {
                int max = 0;

                for(int i = 0; i < 6; i++)
                {
                        
                        if(gs.stonesAt(player, i) == 0 && gs.stonesAt(1-player, i)>0 )
                        {
                                
                                int opponentStone = gs.stonesAt(1-player, i);
                                int move = findReachableMoveToBucket(gs,i,player);
                                if(move!=-1)
                                {
                                        if(opponentStone > max)
                                        {       
                                                max = opponentStone;
                                        }
                                        /* use to debug whether it finds a buckt that can be captured
                                        System.out.println("There is an opening of "+opponentStone+" stones to capture at bucket "+i+" with move "+move);
                                        gs.printState();
                                        */
                                }
                                        
                        }
                }
                
                return max;
        }

        private int findReachableMoveToBucket(MancalaGameState gs, int bucket, int player) {
        	
        		for(int i=0;i<gs.cols();i++)
                {
                        int stone = gs.stonesAt(player, i);
                        if(gs.validMove(i)&&player==0){
                                if(i<bucket){
                                        if((bucket-i)==stone){
                                                return i;
                                        }
                                }
                                if(i>bucket){
                                        if((13-stone)==(bucket-i)){
                                                return i;
                                        }
                                }
                        }
                        if(gs.validMove(i)&&player==1){
                                if(i>bucket){
                                        if((bucket-i)==stone){
                                                return i;
                                        }
                                }
                                if(i<bucket){
                                        if((13-stone)==(bucket-i)){
                                                return i;
                                        }
                                }
                        }
                }
                return -1;
        }

        private int EnemyHasFreeMove(MancalaGameState gs) {
        	MancalaGameState gs_copy = gs.copy();
        	 for(int i=0;i<gs_copy.cols();i++){
                 int stones = gs_copy.stonesAt(gs.CurrentPlayer, i);
                 if(gs.CurrentPlayer==0){
                         if(stones==(gs.cols()-i)%13){
                                 return i;
                                 
                         }
                         
                 }
                 else{
                         if(stones==(i+1)%13){
                                 return i;
                         }
                 }
        	 }
        	 return -1;
        }

		public int ScoreDifference(MancalaGameState gs)
        {
                return  gs.getScore(player)-gs.getScore(1-player);
        }
        
        public int StoneInControl(MancalaGameState gs)
        {
                int number = 0;
                
                for(int i=0;i<gs.cols();i++)
                {
                        number+= gs.stonesAt(player,i)-gs.stonesAt(1-player,i);
                }
                return number;
        }
        
        
        public void findFreeMoves(tNode root){
                for(tNode child: root.children){
                        int stones = root.gs.stonesAt(root.player, child.move);
                        if(root.player==0){
                                if(stones==(root.gs.cols()-child.move)%13){
                                        if(myWeights!=null){
                                                child.heuristic_value += myWeights.getWeight(4)*3;
                                        }
                                        else{
                                                
                                                        child.heuristic_value +=30;
                                                
                                        }
                                        
                                }
                                
                        }
                        else{
                                if(stones==(child.move+1)%13){
                                        if(myWeights!=null){
                                                child.heuristic_value += myWeights.getWeight(4)*3;
                                        }
                                        else{
                                                
                                                        child.heuristic_value +=30;
                                                
                                        }
                                }
                        }
                }

        }

        //auto generated method by implementing MancalaPlayer
        //returns the move the computer selected
        @Override
        public int getMove(MancalaGameState gs) throws Exception {
                // TODO Auto-generated method stub
                tNode root = new tNode(gs);
               // root.generateChildren();
                
                if(gs.validMove(-1)){
                        if(gs.getScore(1-player)>=2){
                                return -1;
                        }
                }
                else{
               // 	 System.out.println("The opponent's last move was"+opponentLastMove(gs));
                }

                alphaBeta(root,1,Integer.MIN_VALUE,Integer.MAX_VALUE,player);
                //System.out.println(minimax(root,0));
                
                System.out.println(root.toString());
             //   root.printChildren();
                int max = Integer.MIN_VALUE;
                int move = -1;
                for(tNode child : root.children){
                        if(child.heuristic_value>=max){
                                max = child.heuristic_value;
                                move = child.move;
                        }
                }
                
             //  tNode temp = root;
            //   anticipatedMove = anticipateMove(temp.getSubChildren(move));
                //MancalaGameState gs_copy = gs.copy();
               // prev = gs_copy.play(move);
               // prev.printState();
               // System.out.println("The anticipatedMove for opponent is"+anticipatedMove);
                return move;   // Tell the game what our final decision is.
        }
        
        public static boolean equals(MancalaGameState first, MancalaGameState other)
    	{
    		int [][] fGrid = first.grid;
    		int [][] sGrid = other.grid;
    		
    		for (int i=0; i<first.NUMROW; i++)
    			for (int j=0; j<first.NUMCOL; j++)
    				if (fGrid[i][j]!=sGrid[i][j])
    					return false;
    		
    		return true;
    	}

        private int opponentLastMove(MancalaGameState gs) throws Exception{
        	MancalaGameState gs_copy = gs.copy();
        	for(int i=0;i<gs_copy.cols();i++){
        		if(gs_copy.validMove(i)){
        			gs_copy = gs_copy.play(i);
        			if(equals(gs_copy,prev)){
        				return i;
        			}
        		}
        		gs_copy = gs.copy();
        	}
        	return -1;
        }
        
        private int anticipateMove(tNode tNode) {
        	int index = -1;
        	int min = Integer.MAX_VALUE;
				for(tNode child : tNode.children){
					if(child.heuristic_value<min){
						min = child.heuristic_value;
						index = child.move;
					}
				}
				return index;
			
		}

		//performs post game actions.
        @Override
        public Object postGameActions(MancalaGameState gs) {
                // TODO Auto-generated method stub
                return null;
        }

        public int getScore() { return score; }
        public void addWin() {score = score +2;}
        public void addDraw() {score = score +1;}
        
        public WeightCollection getWeights() {return myWeights;}

		@Override
		public Object actionsBeforeDeletion() {
			// TODO Auto-generated method stub
			return null;
		}

}